{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "887ae6fb",
   "metadata": {},
   "source": [
    "(nuclio-real-time-functions)=\n",
    "# Nuclio real-time functions\n",
    "\n",
    "Nuclio is a high-performance \"serverless\" framework focused on data, I/O, and compute intensive workloads. It is well integrated with popular \n",
    "data science tools, such as Jupyter and Kubeflow; supports a variety of data and streaming sources; and supports execution over CPUs and GPUs. \n",
    "\n",
    "You can use Nuclio through a fully managed application service (in the cloud or on-prem) in the Iguazio MLOps Platform. MLRun serving \n",
    "utilizes serverless Nuclio functions to create multi-stage real-time pipelines. \n",
    "\n",
    "The underlying Nuclio serverless engine uses a high-performance parallel processing engine that maximizes the utilization of CPUs and GPUs, \n",
    "supports 13 protocols and invocation methods (for example, HTTP, Cron, Kafka, Kinesis), and includes dynamic auto-scaling for HTTP and \n",
    "streaming. Nuclio and MLRun support the full life cycle, including auto-generation of micro-services, APIs, load-balancing, logging, \n",
    "monitoring, and configuration management—such that developers can focus on code, and deploy to production faster with minimal work.\n",
    "\n",
    "Nuclio is extremely fast: a single function instance can process hundreds of thousands of HTTP requests or data records per second. To learn \n",
    "more about how Nuclio works, see the Nuclio architecture [documentation](https://docs.nuclio.io/en/latest/concepts/architecture.html). \n",
    "\n",
    "Nuclio is secure: Nuclio is integrated with Kaniko to allow a secure and production-ready way of building Docker images at run time.\n",
    "\n",
    "Read more in the [Nuclio documentation](https://docs.nuclio.io/en/latest) and the open-source [MLRun library](https://github.com/mlrun/mlrun).\n",
    "\n",
    "**In this section**\n",
    "- [Nuclio function example](#nuclio-function-example)\n",
    "- [Modifying nuclio functions](#modifying-nuclio-functions)\n",
    "- [API gateway](#api-gateway)\n",
    "- [Creating and managing API gateways using the SDK](#creating-and-managing-api-gateways-using-the-sdk)\n",
    "- [Create an API gateway in the UI](#create-an-api-gateway-in-the-ui)\n",
    "\n",
    "## Runtimes using Nuclio\n",
    "\n",
    "The following MLRun runtimes rely on Nuclio for execution:\n",
    "\n",
    "* `application`\n",
    "* `nuclio`\n",
    "* `serving`\n",
    "* `remote`\n",
    "\n",
    "## Nuclio function example\n",
    "\n",
    "You can create your own Nuclio function, for example a data processing function. For every Nuclio function, by default, there is one worker. See [Number of GPUs](../runtimes/configuring-job-resources.md#number-of-workers-and-gpus).\n",
    "\n",
    "The following code illustrates an example of an MLRun function, of kind 'nuclio', that can be deployed to the cluster."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c9b59b3",
   "metadata": {},
   "source": [
    "Create a file `func.py` with the code of the function: \n",
    "```\n",
    "def handler(context, event):\n",
    "    return \"Hello\"\n",
    "```    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2dcd26e",
   "metadata": {},
   "source": [
    "Create the project and the Nuclio function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "105fb38e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import mlrun"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc620518",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create the project\n",
    "project = mlrun.get_or_create_project(\"nuclio-project\", \"./\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5dda40ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a Nuclio function\n",
    "project.set_function(\n",
    "    func=\"func.py\",\n",
    "    image=\"mlrun/mlrun\",\n",
    "    kind=\"nuclio\",\n",
    "    name=\"nuclio-func\",\n",
    "    handler=\"handler\",\n",
    ")\n",
    "project.save()\n",
    "# Deploy the function in the cluster\n",
    "project.deploy_function(\"nuclio-func\")"
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## Change the Nuclio runtime\n",
    "Nuclio supports multiple runtimes (Python, Node.js, Go, etc.). You can find the full list of supported runtimes in the [Nuclio documentation](https://docs.nuclio.io/en/latest/reference/runtimes/index.html).\n",
    "In order to change the Nuclio runtime of an MLRun function, you can set the `nuclio_runtime` field in the function spec as shown below:\n",
    "\n",
    "```python\n",
    "func = project.set_function(\"./func.py\", name=\"py11\", kind=\"nuclio\", handler=\"handler\")\n",
    "func.spec.nuclio_runtime = \"python:3.11\"\n",
    "func.deploy()\n",
    "```\n",
    "\n",
    "## Modify Nuclio trigger configurations\n",
    "\n",
    "To modify a trigger configuration in a Nuclio-based function, you can access the Nuclio-based trigger configuration by `spec.triggers.<trigger_name>`.\n",
    "For example, to set the HTTP trigger mode to `async`, you can do the following:\n",
    "```python\n",
    "func = project.set_function(\n",
    "    func=f\"func.py\", name=\"func\", image=\"python:3.9\", kind=\"nuclio\", handler=\"handler\"\n",
    ")\n",
    "func.with_http()\n",
    "func.spec.config[\"spec.triggers.http\"][\"mode\"] = \"async\"\n",
    "```\n",
    "All possible trigger configurations are available in the [Nuclio documentation](https://docs.nuclio.io/en/latest/reference/triggers/index.html)."
   ],
   "id": "5decb70b1c0ca999"
  },
  {
   "cell_type": "markdown",
   "id": "106bc9dc",
   "metadata": {},
   "source": [
    "See more examples in {ref}`create-and-use-functions`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff7c7a91",
   "metadata": {},
   "source": [
    "## Modifying Nuclio functions\n",
    "\n",
    "When working with Nuclio functions (remote / serving / application runtimes) and API gateways in MLRun, any modifications/deletions should be made in MLRun only. This is also relevant for modifying the spec of a function or API gateway. \n",
    "<b>Changes that are made in Nuclio are not guaranteed to be synced to MLRun and can cause unexpected behavior.</b> \n",
    "\n",
    "For example, if you create a remote function in MLRun (which is then created in Nuclio) and then delete it in Nuclio, the function status might show as ready, but invoking it will fail.\n",
    "\n",
    "If you get an error similar to: </br>`Error when translating nuclio names to mlrun names in api gateway: number of functions doesn't match the mlrun functions in annotation`</br>\n",
    "it means that the API gateway was updated at least once manually in Nuclio itself, without using the MLRun SDK. To rectify this situation, delete the gateway, and then create it again.\n",
    "\n",
    "## API gateway\n",
    "\n",
    "```{admonition} Note\n",
    "Since API Gateway is only a Nuclio feature, it is supported only for Nuclio runtimes.\n",
    "```\n",
    "API gateway is an entity that Nuclio creates on top of one or two functions. From a Kubernetes perspective, an API gateway is just an ingress that links to the Nuclio function. If an API gateway is created on top of two functions, it's a {ref}`canary function<canary>`. The other feature that API gateways provide is authorization. Read more about API gateways in the [Nuclio documentation](https://docs.nuclio.io/en/latest/reference/api-gateway/index.html).\n",
    "\n",
    "## Creating and managing API gateways using the SDK\n",
    "\n",
    "```{admonition} Note\n",
    "Requires Nuclio v1.13.1 or higher.\n",
    "```\n",
    "MLRun SDK:\n",
    "- {py:meth}`~mlrun.runtimes.ApplicationRuntime.create_api_gateway`\n",
    "- {py:meth}`~mlrun.projects.MlrunProject.store_api_gateway`\n",
    "- {py:meth}`~mlrun.projects.MlrunProject.delete_api_gateway`\n",
    "- {py:meth}`~mlrun.projects.MlrunProject.get_api_gateway`\n",
    "- {py:meth}`~mlrun.projects.MlrunProject.list_api_gateways`\n",
    "\n",
    "\n",
    "### Create an API gateway with basic authorization\n",
    "\n",
    "Assume you already deployed a nuclio function:\n",
    "\n",
    "```\n",
    "fn = project.set_function(\n",
    "            func=\"nuclio_function.py\",\n",
    "            name=f\"nuclio-func\",\n",
    "            kind=\"nuclio\",\n",
    "            image=\"python:3.9\",\n",
    "            handler=\"handler\",\n",
    "        )\n",
    "```\t"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e1bfe1a",
   "metadata": {},
   "source": [
    "Define the API gateway entity:\n",
    "```\n",
    "my_api_gateway = mlrun.runtimes.nuclio.api_gateway.APIGateway(\n",
    "            mlrun.runtimes.nuclio.api_gateway.APIGatewayMetadata(\n",
    "                name=\"gw-with-basic-auth\",\n",
    "            ),\n",
    "            mlrun.runtimes.nuclio.api_gateway.APIGatewaySpec(\n",
    "                functions=fn,\n",
    "                project=project.name,\n",
    "            ),\n",
    "    )\n",
    "```\n",
    "Add basic authorization configuration:\n",
    "```\n",
    "my_api_gateway.with_basic_auth(\n",
    "    username=\"test\",\n",
    "    password=\"pass\",\n",
    ")\n",
    "```\n",
    "\n",
    "Add an access key authentication that authenticates users via the Iguazio backend:\n",
    "```\n",
    "my_api_gateway.with_access_key_auth()\n",
    "```\n",
    "Create (or update) the API gateway</br>\n",
    "It's crucial to update the defined API Gateway entity with the entity returned from the `store_api_gateway` method. This fills in important fields like host/path, etc.\n",
    "```\n",
    "my_api_gateway = project.store_api_gateway(my_api_gateway)\n",
    "```\n",
    "`store_api_gateway` checks that api_gateway is ready to be invoked before returning it. You can also check if api gateway is in ready state:\n",
    "```\n",
    "my_api_gateway.is_ready()\n",
    "```\n",
    "\n",
    "If you know that the API gateway was changed (for example, in another jupyter notebook), load the latest changes:\n",
    "```\n",
    "my_api_gateway.sync()\n",
    "```\n",
    "\n",
    "Invoke the API gateway</br>\n",
    "Since the gateway is configured with basic auth, you need to pass authorization credentials:\n",
    "```\n",
    "response = my_api_gateway.invoke(auth=(\"test\", \"pass\"), verify=False)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8132515",
   "metadata": {},
   "source": [
    "### Updating an API gateway\n",
    "Add basic auth on top the created API gateway \n",
    "```\n",
    "my_api_gateway.with_basic_auth(\"test\", \"pass\")\n",
    "```\n",
    "\n",
    "Change the canary function percentages\n",
    "```\n",
    "my_api_gateway.with_canary([fn1, fn2], [10, 90])\n",
    "```\n",
    "\n",
    "Update the API gateway\n",
    "```\n",
    "my_api_gateway = project.store_api_gateway(my_api_gateway)\n",
    "```\n",
    "\n",
    "## Create an API gateway in the UI\n",
    "\n",
    "To create an API gateway in the UI:\n",
    "1. In your project page, press **API Gateways** tab, then press **NEW API GATEWAY**.\n",
    "2. Select an **Authentication Mode**:\n",
    "   - None (default)\n",
    "   - Basic\n",
    "   - Access key\n",
    "   - OAuth2 (Not supported yet in MLRun SDK)\n",
    "   \n",
    "   and fill in any required values.\n",
    "2. Type in the API Gateway parameters:\n",
    "   - **Name**: The name of the API Gateway. Required\n",
    "   - **Description**: A description of the API Gateway.\n",
    "   - **Host**: The host of the API Gateway. (Relevant for open-source only.)\n",
    "   - **Path**: The path of the API Gateway.\n",
    "2. In **Primary**, type in the function that is triggered via the API Gateway. \n",
    "\n",
    "```{admonition} Note\n",
    "Do not change the configuration by using the UI: it breaks the functionality.\n",
    "```\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
